package com.h2.ref.server.user.relationship.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.h2.ref.server.user.relationship.UserRelationship;

public class UserRelationshipDaoInMem {
   
   private Integer _nextId = 0;
   
   private Map<Integer, UserRelationship> _relationships;
   
   /** Map<userId, Collection<relationshipId>> */
   private Map<Integer, Collection<Integer>> _relationshipsTo;
   
   /** Map<userId, relationshipId> */
   private Map<Integer, Collection<Integer>> _relationshipsFrom;
   
   ////
   ////
   
   public void init() {
      setRelationships(new HashMap<Integer, UserRelationship>());
      setRelationshipsTo(new HashMap<Integer, Collection<Integer>>());
      setRelationshipsFrom(new HashMap<Integer, Collection<Integer>>());
   }
   
   public synchronized Boolean create(UserRelationship relationship) {
      while(getRelationships().containsKey(getNextId())) {
         setNextId(getNextId() + 1);
      }
      relationship.setId(getNextId());
      Collection<Integer> relationships;
      
      //to
      if (getRelationshipsTo().containsKey(relationship.getActorId())) {
         relationships = getRelationshipsTo().get(relationship.getActorId());
      }
      else {
         relationships = new ArrayList<Integer>();
         getRelationshipsTo().put(relationship.getActorId(), relationships);
      }
      relationships.add(getNextId());
      
      //from
      if (getRelationshipsFrom().containsKey(relationship.getTargetId())) {
         relationships = getRelationshipsFrom().get(relationship.getTargetId());
      }
      else {
         relationships = new ArrayList<Integer>();
         getRelationshipsFrom().put(relationship.getTargetId(), relationships);
      }
      relationships.add(getNextId());
      getRelationships().put(getNextId(), relationship);
      return Boolean.TRUE;
   }
   
   public Collection<UserRelationship> find(UserRelationship relationship) {
      Collection<UserRelationship> results = findHelper(relationship);
      
      if (relationship.getRelationshipType() != null) {
         for (Iterator<UserRelationship> iterator = results.iterator();
                  iterator.hasNext();) {
            UserRelationship userRelationship = iterator.next();
            if (!relationship.getRelationshipType().equals(
                  userRelationship.getRelationshipType())) {
               iterator.remove();
            }
         }
      }
      return results;
   }
   
   public Collection<UserRelationship> findHelper(
         UserRelationship relationship) {
      
      if (relationship.getActorId() != null) {
         Collection<Integer> actorRelationships = 
               getRelationshipsTo().get(relationship.getActorId());
         if (actorRelationships == null || actorRelationships.isEmpty()) {
            //nothing matched
            return Collections.emptyList();
         }
         
         if (relationship.getTargetId() != null) {
            //found both IDs
            Collection<Integer> targetRelationships = 
                  getRelationshipsFrom().get(relationship.getTargetId());
            if (targetRelationships == null || targetRelationships.isEmpty()) {
               //nothing matched
               return Collections.emptyList();
            }
            
            //match found
            actorRelationships.retainAll(targetRelationships);
            Set<Integer> noDups = new TreeSet<Integer>(actorRelationships);
            if (noDups == null || noDups.isEmpty()) {
               //nothing matched
               return Collections.emptyList();
            }
            
            Collection<UserRelationship> results =
                  new ArrayList<UserRelationship>();
            for (Integer relationshipId : noDups) {
               results.add(getRelationships().get(relationshipId));
            }
            return results;
         }
         else {
            //found only actor ID
            
            Collection<UserRelationship> results =
                  new ArrayList<UserRelationship>();
            for (Integer relationshipId : actorRelationships) {
               results.add(getRelationships().get(relationshipId));
            }
            return results;
         }
      }
      else if (relationship.getTargetId() != null) {
         Collection<Integer> targetRelationships = 
               getRelationshipsFrom().get(relationship.getTargetId());
         if (targetRelationships == null || targetRelationships.isEmpty()) {
            //nothing matched
            return Collections.emptyList();
         }
         
         Collection<UserRelationship> results =
               new ArrayList<UserRelationship>();
         for (Integer relationshipId : targetRelationships) {
            results.add(getRelationships().get(relationshipId));
         }
         return results;
      }
      return Collections.emptyList();
   }
   
   public Boolean update(UserRelationship relationship) {
      UserRelationship match = getRelationships().get(relationship.getId());
      if (match == null) {
         return Boolean.FALSE;
      }
      
      //TODO
      return Boolean.FALSE;
   }
   
   public Boolean delete(UserRelationship relationship) {
      Collection<UserRelationship> matches = find(relationship);
      if (matches == null || matches.isEmpty()) {
         return Boolean.FALSE;
      }
      for (UserRelationship userRelationship : matches) {
         //relationships
         getRelationships().remove(userRelationship.getId());
         
         //to relationships
         Collection<Integer> relationships = getRelationshipsTo().get(
               userRelationship.getActorId());
         relationships.remove(userRelationship.getId());
         
         //from relationships
         relationships = getRelationshipsFrom().get(
               userRelationship.getTargetId());
         relationships.remove(userRelationship.getId());
      }
      return Boolean.TRUE;
   }

   /**
    * @return the nextId
    */
   public Integer getNextId() {
      return _nextId;
   }

   /**
    * @param nextId the nextId to set
    */
   public void setNextId(Integer nextId) {
      _nextId = nextId;
   }

   /**
    * @return the relationships
    */
   public Map<Integer, UserRelationship> getRelationships() {
      return _relationships;
   }

   /**
    * @param relationships the relationships to set
    */
   public void setRelationships(Map<Integer, UserRelationship> relationships) {
      _relationships = relationships;
   }

   /**
    * @return the relationshipsTo
    */
   public Map<Integer, Collection<Integer>> getRelationshipsTo() {
      return _relationshipsTo;
   }

   /**
    * @param relationshipsTo the relationshipsTo to set
    */
   public void setRelationshipsTo(Map<Integer, Collection<Integer>> relationshipsTo) {
      _relationshipsTo = relationshipsTo;
   }

   /**
    * @return the relationshipsFrom
    */
   public Map<Integer, Collection<Integer>> getRelationshipsFrom() {
      return _relationshipsFrom;
   }

   /**
    * @param relationshipsFrom the relationshipsFrom to set
    */
   public void setRelationshipsFrom(
         Map<Integer, Collection<Integer>> relationshipsFrom) {
      _relationshipsFrom = relationshipsFrom;
   }

}
